using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using org.tmatesoft.svn.core;
using org.tmatesoft.svn.core.wc;

namespace Subsync.Core.Subversion
{
	/// <summary>
	/// Author: Steven Goh Jian Wen, some methods redone by Andy
	/// </summary>
	public class CLIManager : App
	{
		public override Dictionary<string, List<object>> GetTokens()
		{
			Dictionary<string, List<object>> ret = new Dictionary<string, List<object>> ();

			//For Config testing
			if (MainClass.debug) {
				ret.Add("add", new List<object>() { "add" });
				ret.Add("adminCreate", new List<object>() { "adminCreate" });
	            ret.Add("checkout",new List<object>() {"co","checkout"});
				ret.Add("Cleanup",new List<object>() {"cu","cleanup"});
				ret.Add("commit", new List<object>() { "ci", "commit" });
	            ret.Add("import",new List<object>() {"im","import"});
                ret.Add("info", new List<object>() { "i", "info" });
				ret.Add("Lock",new List<object>() {"lk","lock"});
				ret.Add("mkdir", new List<object>() { "mkdir" });
	            ret.Add("move",new List<object>() {"mv","move"});
				ret.Add("propdel",new List<object>() {"pd","propdel"});
				ret.Add("propset",new List<object>() {"ps","propset"});
				ret.Add("remove",new List<object>() {"rm","remove"});
				ret.Add("Resolve",new List<object>() {"rs","resolve"});
				ret.Add("revert",new List<object>() {"rv","revert"});
				ret.Add("Status",new List<object>() {"st","status"});
				ret.Add("Switch",new List<object>() {"sw","switch"});
				ret.Add("Unlock",new List<object>() {"ul","unlock"});
				ret.Add("update",new List<object>() {"ud","update"});
			}
			return ret;
		}
		
		public override List<KeyValuePair<string, initPointer>> GetInitPointers ()
		{
			List<KeyValuePair<string, initPointer>> ret = new List<KeyValuePair<string, initPointer>> ();

			//For Config testing
			if (MainClass.debug) {
                ret.Add(new KeyValuePair<string,initPointer>("add", new initPointer (this.SVNAdd)));
			    ret.Add(new KeyValuePair<string,initPointer>("adminCreate", new initPointer(this.SVNAdminCreate)));
                ret.Add(new KeyValuePair<string,initPointer>("checkout",new initPointer(this.SVNCheckout)));
			    ret.Add(new KeyValuePair<string,initPointer>("Cleanup",new initPointer(this.SVNCleanup)));
			    ret.Add(new KeyValuePair<string,initPointer>("commit", new initPointer(this.SVNCommit)));
                ret.Add(new KeyValuePair<string,initPointer>("import",new initPointer(this.SVNImport)));
                ret.Add(new KeyValuePair<string,initPointer>("info", new initPointer(this.SVNInfo)));
			    ret.Add(new KeyValuePair<string,initPointer>("Lock",new initPointer(this.SVNLock)));
			    ret.Add(new KeyValuePair<string,initPointer>("mkdir", new initPointer(this.SVNMkdir)));
                ret.Add(new KeyValuePair<string,initPointer>("move",new initPointer(this.SVNMove)));
			    ret.Add(new KeyValuePair<string,initPointer>("propdel",new initPointer(this.SVNPropDel)));
			    ret.Add(new KeyValuePair<string,initPointer>("propset",new initPointer(this.SVNPropSet)));
			    ret.Add(new KeyValuePair<string,initPointer>("remove",new initPointer(this.SVNRemove)));
			    ret.Add(new KeyValuePair<string,initPointer>("resolve",new initPointer(this.SVNResolve)));
			    ret.Add(new KeyValuePair<string,initPointer>("revert",new initPointer(this.SVNRevert)));
			    ret.Add(new KeyValuePair<string,initPointer>("Status",new initPointer(this.SVNStatus)));
			    ret.Add(new KeyValuePair<string,initPointer>("Switch",new initPointer(this.SVNSwitch)));
			    ret.Add(new KeyValuePair<string,initPointer>("Unlock",new initPointer(this.SVNUnlock)));
			    ret.Add(new KeyValuePair<string,initPointer>("update",new initPointer(this.SVNUpdate)));
			}
			return ret;
		}
		
		/// <summary>
		/// Usage: subsync.exe --newsync/-n <path_to_main_folder_to_be_synced> <sync-name: mp31>
		/// </summary>
		/// <param name="tokens">
		/// A <see cref="Dictionary<System.String, List<System.Object>>"/>
		/// </param>
		public void SVNAdd(Dictionary<string, List<object>> tokens)
        {
            if (tokens["add"].Count != 1)
            {
                Console.WriteLine("Not enough arguments: Add requires at least 1 arguments (file/folder location)");
                return;
            }
            string location = (string)tokens["add"][0];

            try
            {
                new SVNAdd(location, false).Execute();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
		
		public void SVNAdminCreate(Dictionary<string, List<object>> tokens)
        {
            if (tokens["adminCreate"].Count != 1)
            {
                Console.WriteLine("Not enough arguments: AdminCreate requires at least 1 arguments (location)");
                return;
            }
            string location = (string)tokens["adminCreate"][0];

            try
            {
                new SVNAdminCreate(location).Execute();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
		
		public void SVNCheckout(Dictionary<string,List<object>> tokens)
        {
            if (tokens["checkout"].Count < 1)
            {
                Console.WriteLine("Not enough arguments: Checkout requires at least 1 argument (url, <optional> destPath)");
                return;
            }
            string url = (string)tokens["checkout"][0];

            try
            {
                switch (tokens["checkout"].Count)
                {
                    case (1):
                        new SVNCheckout(url).Execute();
                        break;
                    case (2):
                        string destPath = tokens["checkout"][1].ToString();
                        new SVNCheckout(url, destPath).Execute();
                        break;
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
		}
		
		public void SVNCleanup(Dictionary<string,List<object>> tokens) 
        {
            if (tokens["Cleanup"].Count == 0)
            {
                Console.WriteLine("Not enough arguments: Cleanup requires at least 1 argument (path)");
                return;
            }
            string strPath = (string)tokens["Cleanup"][0];

            try
            {
                new SVNCleanup(strPath).Execute();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
		
		public void SVNCommit(Dictionary<string, List<object>> tokens)
        {
            if (tokens["commit"].Count != 1)
            {
                Console.WriteLine("Not enough arguments: Commit requires at least 1 argument (working copy location)"); ;
                return;
            }
            string wcpath = (string)tokens["commit"][0];

            try
            {
                new SVNCommit(wcpath).Execute();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
		
		public void SVNImport(Dictionary<string,List<object>> tokens)
		{
            if (tokens["import"].Count < 2)
            {
                Console.WriteLine("2 arguments required - WC path, new repo path");
                return;
            }

            try
            {
                new SVNImport((string)tokens["import"][0], (string)tokens["import"][1]).Execute();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
		}

        public void SVNInfo(Dictionary<string, List<object>> tokens)
        {
            if (tokens["info"].Count != 1 && tokens["info"].Count != 2 && tokens["info"].Count != 3)
            {
                Console.WriteLine("Invalid arguments");
                return;
            }

            try
            {
                SVNInfo helper = new SVNInfo();
                helper.handleInfo(tokens);
                helper.Execute();
                Console.WriteLine(helper.revInfo);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
		
		public void SVNLock(Dictionary<string,List<object>> tokens) {
            int no_of_path = tokens["Lock"].Count - 1;

            if (no_of_path < 1)
            {
                Console.WriteLine("Not enough arguments: Lock requires at least 2 argument (url, message)");
                return;
            }
            List<string> path = new List<string>();

            for (int i = 0; i < no_of_path; i++)
                path.Add((string)tokens["Lock"][i]);

            string message = (string)tokens["Lock"][no_of_path];

            try
            {
                new SVNLock(path, true, message).Execute();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
		
		
		public void SVNMkdir(Dictionary<string, List<object>> tokens)
        {
            if (tokens["mkdir"].Count < 1)
            {
                Console.WriteLine("Not enough arguments: Mkdir requires at least 1 argument (new folder name)");
                return;
            }
            string[] urls = new string[1];
            string url = "";
            bool WC = false;

            try
            {
                for (int i = 0; i < tokens["mkdir"].Count; i++)
                {
                    url = (string)tokens["mkdir"][i];
                    WC = CheckPath(url);
                    if (!WC)
                    {
                        urls[0] = url;
                        SVNMkdir helper = new SVNMkdir(urls, "");
                        helper.Execute();
                        Console.WriteLine("Created directory {0}", url);
                    }
                    else
                    {
                        try
                        {
                            if (Directory.Exists(url))
                            {
                                Console.WriteLine("{0} already exists.", url);
                                continue;
                            }
                            Directory.CreateDirectory(url);
                            new SVNAdd(new java.io.File(url), false, false, false, SVNDepth.INFINITY, true, true).Execute();
                            Console.WriteLine("Created directory {0}", url);
                        }
                        catch (SVNException e)
                        {
                            throw new SVNExecuteException(e.getMessage());
                        }
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
		
		public void SVNMove(Dictionary<string,List<object>> tokens)
        {
            if (tokens["move"].Count < 2)
            {
                Console.WriteLine("Not enough arguments: move requires at least 2 arguments"); ;
                return;
            }
            string source = (string)tokens["move"][0];
            string destination = (string)tokens["move"][1];

            try
            {
                new SVNMove(source, destination).Execute();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
		}
		
		public void SVNPropDel(Dictionary<string,List<object>> tokens)
        {
            if (tokens["propdel"].Count < 2)
            {
                Console.WriteLine("Not enough arguments: propdel requires at least 3 arguments");
                return;
            }
            string helperpath = (string)tokens["propdel"][0];
            string helpername = (string)tokens["propdel"][1];

            try
            {
                new SVNPropDel(helperpath, helpername).Execute();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
		}
		
		public void SVNPropSet(Dictionary<string,List<object>> tokens)
        {
            if (tokens["propset"].Count < 3)
            {
                Console.WriteLine("Not enough arguments: propset requires at least 3 arguments");
                return;
            }
            string helperpath = (string)tokens["propset"][0];
            string helpername = (string)tokens["propset"][1];
            string helpervalue = (string)tokens["propset"][2];

            try
            {
                new SVNPropSet(helperpath, helpername, helpervalue).Execute();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
		}
		
		public void SVNRemove(Dictionary<string,List<object>> tokens)
		{
            string path;

            if (tokens["remove"].Count < 1)
            {
                Console.WriteLine("Remove requires one path");
                return;
            }
            path = (string)tokens["remove"][0];

            try
            {
                new SVNRemove(path).Execute();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
		}
		
		public void SVNResolve(Dictionary<string,List<object>> tokens)
        {
            if (tokens["resolve"].Count < 2)
            {
                Console.WriteLine("Resolve requires 2 argument - WC filepath, choice");
                return;
            }
            string fpath = (string)tokens["resolve"][0];

            try
            {                
                if (string.Equals((string)tokens["resolve"][1], "mine"))
                    new SVNResolve(fpath, Resolution.MineFully).Execute();
                else if (string.Equals((string)tokens["resolve"][1], "theirs"))
                    new SVNResolve(fpath, Resolution.TheirsFully).Execute();
                else
                    Console.WriteLine("Invalid choice, either mine or theirs");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
		
		public void SVNRevert(Dictionary<string,List<object>> tokens) 
		{
            int no_of_path = tokens["revert"].Count;
            if (no_of_path == 0)
            {
                Console.WriteLine("Revert requires at least one path");
                return;
            }
            string[] path = new string[no_of_path];

            for (int i = 0; i < no_of_path; i++)
                path[i] = (string)tokens["revert"][i];

            try
            {
                new SVNRevert(path).Execute();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
		}
		
		public void SVNStatus(Dictionary<string,List<object>> tokens)
        {
            if (tokens["Status"].Count == 0)
            {
                Console.WriteLine("Not enough arguments: Status requires at least 1 argument (path)");
                return;
            }
            string path = (string)tokens["Status"][0];

            try
            {
                new SVNStatus(path).Execute();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
		
		public void SVNSwitch(Dictionary<string,List<object>> tokens)
        {
            if (tokens["Switch"].Count != 1)
            {
                Console.WriteLine("Not enough arguments: Switch requires at least 1 argument (url)");
                return;
            }
            string url = (string)tokens["Switch"][0];
            
            try
            {
                new SVNSwitch(url).Execute();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
		
		public void SVNUnlock(Dictionary<string,List<object>> tokens)
        {
            int no_of_path = tokens["Unlock"].Count;
            if (no_of_path == 0)
            {
                Console.WriteLine("Not enough arguments: Lock requires at least 1 argument - path");
                return;
            }

            List<string> path = new List<string>();

            for (int i = 0; i < no_of_path; i++)
                path.Add((string)tokens["Unlock"][i]);

            try
            {
                new SVNUnlock(path, true).Execute();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
		
		public void SVNUpdate(Dictionary<string,List<object>> tokens)
        {
            if (tokens["update"].Count == 0)
            {
                Console.WriteLine("Not enough arguments: Update requires at least 1 argument (filepath)");
                return;
            }
            string filepath = (string)tokens["update"][0];
            
            try
            {
                new SVNUpdate(filepath).Execute();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        // Helper Methods
        public bool CheckPath(string inputPath)
        {
            // Checks if path is for repos access or WC access
            // Returns true for WC access
            List<string> repos_access = new List<string>();
            // List of Repos access URLs
            repos_access.Add("file:///"); // Local repos
            repos_access.Add("http://");
            repos_access.Add("https://");
            repos_access.Add("svn://");
            repos_access.Add("svn+ssh://");

            foreach (string accessprefix in repos_access)
            {
                if (inputPath.StartsWith(accessprefix))
                    return false;
            }
            return true;
        }
	}
}